﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace UsersProvider.Repositories.RelationalRepositories
{
    public class UsersRepository : IUsersRepository
    {
        //ToDo: reimplement to UserExists: bool
        public User GetUserByLoginAndPassword(string login, string password)
        {
            using (UsersModelEntities context = new UsersModelEntities())
            {
                return context.UserSet.FirstOrDefault(u => u.Login == login && u.PassHash == password);
            }
        }

        /// <summary>
        /// Returns the user which is associated with an openid specified
        /// </summary>
        /// <param name="openIdURL">OpenId URL</param>
        /// <returns></returns>
        public User GetUserByEmail(string email)
        {
            using (UsersModelEntities context = new UsersModelEntities())
            {
                return context.UserSet.FirstOrDefault(user => user.Email == email);
            }
        }

        public User GetUserByLogin(string login)
        {
            using (UsersModelEntities context = new UsersModelEntities())
            {
                return context.UserSet.FirstOrDefault(user => user.Login == login);
            }
        }

        /// <summary>
        /// Returns the user which is associated with an openid specified
        /// </summary>
        /// <param name="openIdURL">OpenId URL</param>
        /// <returns></returns>
        public User GetUserByConfirmationId(string confirmationId)
        {
            using (UsersModelEntities context = new UsersModelEntities())
            {
                return context.UserSet.FirstOrDefault(user => user.ConfirmationId == confirmationId);
            }
        }

        /// <summary>
        /// Returns the user which is associated with an openid specified
        /// </summary>
        /// <param name="openIdURL">OpenId URL</param>
        /// <returns></returns>
        public User GetUserFromOpenId(string openIdURL)
        {
            using (UsersModelEntities context = new UsersModelEntities())
            {
                return context.UserSet.FirstOrDefault(user => user.OpenIds.Any(openId => openId.URL == openIdURL));
            }
        }

        /// <summary>
        /// Returns true if the user with the specified login exists
        /// </summary>
        /// <param name="username">Username to check</param>
        /// <returns></returns>
        public bool LoginExists(string login)
        {
            using (UsersModelEntities context = new UsersModelEntities())
            {
                return context.UserSet.Any(user => user.Login == login);
            }
        }

        /// <summary>
        /// Returns true if the user with the specified login exists
        /// </summary>
        public bool EmailExists(string email)
        {
            using (UsersModelEntities context = new UsersModelEntities())
            {
                return context.UserSet.Any(user => user.Email == email);
            }
        }

        /// <summary>
        /// Inserts a new user into the DB
        /// </summary>
        /// <param name="login">User login</param>
        /// <param name="passMD5">User password hash</param>
        /// <exception cref="UsersProvider.RepositoryOperationFailedException"></exception>
        public void InsertUser(string login, string pass, string email, string confirmationId, string lang)
        {
            using (UsersModelEntities context = new UsersModelEntities())
            {
                if (!context.UserSet.Any(user => user.Login == login))
                {
                    User newUser = new User
                    {
                        Login = login,
                        PassHash = pass,
                        Email = email,
                        ConfirmationId = confirmationId,
                        RegistrationDate = DateTime.Now,
                        PreferredLanguage = lang,
                        Role = context.RoleSet.First(),
                    };
                    context.AddToUserSet(newUser);
                    context.SaveChanges();
                }
                else
                {
                    throw new RepositoryOperationFailedException("User already exists in the DB");
                }
            }
        }

        public User ApproveUser(string confirmationId)
        {
            using (UsersModelEntities context = new UsersModelEntities())
            {
                User user = context.UserSet.FirstOrDefault(u => u.ConfirmationId == confirmationId);
                if (user == null) return null;

                user.ConfirmationId = null;
                context.SaveChanges();
                return user;
            }
        }

        /// <summary>
        /// Returns all OpenId accounts associated with the user specified
        /// </summary>
        /// <param name="login">User login</param>
        /// <returns></returns>
        public IEnumerable<OpenId> GetUserOpenIds(string login)
        {
            using (UsersModelEntities context = new UsersModelEntities())
            {
                User user = context.UserSet.Where(usr => usr.Login == login).FirstOrDefault();
                if (user == null)
                {
                    throw new RepositoryOperationFailedException("User doesn't exist in the database");
                }

                return user.OpenIds;
            }
        }

        /// <summary>
        /// Adds the user's OpenId association
        /// </summary>
        /// <param name="login"></param>
        /// <param name="passMd5"></param>
        /// <param name="openIdUrl"></param>
        public void AddUserOpenId(string login, string passMd5, string openIdUrl, string lang)
        {
            using (UsersModelEntities context = new UsersModelEntities())
            {
                User user = context.UserSet.Where(usr => usr.Login == login).FirstOrDefault();

                OpenId openId = context.OpenIdSet.Where(id => id.URL == openIdUrl).FirstOrDefault();

                if (user == null)
                {
                    user = new User
                    {
                        Login = login,
                        PassHash = passMd5,
                        Email = string.Empty,
                        RegistrationDate = DateTime.Now,
                        Role = context.RoleSet.First(),
                        PreferredLanguage = lang
                    };
                    context.AddToUserSet(user);
                }
                else if (user.PassHash != passMd5)
                {
                    ///TODO: Localize error message
                    throw new RepositoryOperationFailedException("Password is invalid");
                }

                if (openId == null)
                {
                    openId = new OpenId { URL = openIdUrl };
                    context.AddToOpenIdSet(openId);
                }

                if (!user.OpenIds.Contains(openId))
                {
                    user.OpenIds.Add(openId);
                }

                context.SaveChanges();
            }
        }

        public void SetUserLanguage(string login, string lang)
        {
            using (UsersModelEntities context = new UsersModelEntities())
            {
                var entity = context.UserSet.First(usr => usr.Login == login);
                entity.PreferredLanguage = lang;
                context.SaveChanges();
            }
        }
    }
}